#include "Sock.hpp"
#include <iostream>
#include <sys/epoll.h>

#define SIZE 128
#define NUM  64

void Usage(char* proc){
    std::cout << "Usage: \n\t" << proc << " port" << std::endl;
}


// ./epoll_server port
int main(int argc, char* argv[]){
    if (argc != 2){
        Usage(argv[0]);
        return 1;
    }
    // 1.建立tcp 监听socket
    uint16_t port = (uint16_t)atoi(argv[1]);
    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, port);
    Sock::Listen(listen_sock);

    // 2.创建epoll模型，获取epfd（文件描述符）
    int epfd = epoll_create(SIZE);

    // 3.先将listen_sock和它所关心的事件，添加到内核
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = listen_sock;
    epoll_ctl(epfd, EPOLL_CTL_ADD, listen_sock, &ev);

    // 4.事件循环
    volatile bool quit = false;
    struct epoll_event revs[NUM];
    while (!quit){
        int timeout = -1;
        // 这里传入的数组，仅仅是尝试从内核中拿回来就已经就绪的事件
        int n = epoll_wait(epfd, revs, NUM, timeout);
        switch(n){
            case 0:  std::cout << "time out ..." << std::endl; break;
            case -1: std::cerr << "epoll_wait error" << std::endl; break;
            default: std::cout << "有事件就绪了..." << std::endl;
            // 5.处理就绪事件
            for (int i = 0; i < n; i++){
                int sock = revs[i].data.fd; // 暂时方案
                std::cout << "文件描述符: " << sock << "上有事件就绪了" << std::endl;
                if (revs[i].events & EPOLLIN){
                    // 5.1处理链接事件
                    std::cout << "文件描述符: " << sock << "上读事件就绪了" << std::endl;
                    if (sock == listen_sock){
                        std::cout << "文件描述符: " << sock << "上链接事件就绪了" << std::endl;
                        int fd = Sock::Accept(listen_sock);
                        if (fd > 0){
                            // 有链接到来并不代表有数据到，所以不能立即读取
                            std::cout << "获取新连接成功,分配fd: " << fd << std::endl;
                            struct epoll_event _ev;
                            _ev.events = EPOLLIN;
                            _ev.data.fd = fd;
                            epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &_ev); // 新的fd托管给epoll
                            std::cout << fd << " 交给epoll托管" << std::endl;
                        }
                        else {
                            // do nothing   
                        }
                    }
                    else {
                        std::cout << "文件描述符: " << sock << "上普通读事件就绪了" << std::endl;
                        char buffer[1024];
                        ssize_t s = recv(sock, buffer, sizeof(buffer) -1, 0);
                        if (s > 0){
                            buffer[s] = 0;
                            std::cout << "client[" << sock << "]# " << buffer << std::endl;

                            //将关心的事件改为EPOLLOUT
                            // struct epoll_event _ev;
                            // _ev.events = EPOLLOUT;
                            // _ev.data.fd = sock;
                            // epoll_ctl(epfd, EPOLL_CTL_MOD, sock, &_ev);
                        }
                        else if (s == 0){
                            // 对端关闭连接
                            std::cout << "client quit " << sock << std::endl;
                            close(sock);
                            epoll_ctl(epfd, EPOLL_CTL_DEL, sock, nullptr);
                            std::cout << "sock" << sock << "delete from epoll success" << std::endl;
                        }
                        else {
                            std::cerr << "recv error" << errno << std::endl;
                            close(sock);
                            epoll_ctl(epfd, EPOLL_CTL_DEL, sock, nullptr);
                            std::cout << "sock: " << sock << "delete from epoll success" << std::endl;
                        }
                    }
                }
                else if (revs[i].events & EPOLLOUT) { 
                    // 5.2处理写事件

                }
                else {

                }
            }
            break;
        }
    }
    close(epfd);
    close(listen_sock);
}